﻿//Copyright 2011 - Jonathan Allen

using System;
using System.Linq;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;

namespace Granite.Collections
{

 //   /// <summary>
 //   /// Helper methods for creating linked collections
 //   /// </summary>
 //   [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
 //   public static class LinkedCollection
 //   {
 //       /// <summary>
 //       /// This creates a target observable collection that is kept in sync with a parent collection of a different type. 
 //       /// </summary>
 //       /// <typeparam name="TSource">Type of item stored in the source collection</typeparam>
 //       /// <typeparam name="TTarget">Type of item stored in the target collection</typeparam>
 //       /// <param name="source">Source collection to be watched</param>
 //       /// <param name="convert">function that converts items in the source into items for the target</param>
 //       /// <param name="convertBack">function that converts items in the target into items for the source</param>
 //       /// <returns>Target collection to be watched</returns>
 //       /// <remarks>Ensure BreakLink is called on the target collection before it loses scope. Otherwise the source collection will continue sending updates to the target collection.</remarks>
 //       public static LinkedCollection<TTarget> Link<TSource, TTarget>(this ObservableCollection<TSource> source, Func<TSource, TTarget> convert, Func<TTarget, TSource> convertBack)
 //       {
 //           if (source == null)
 //               throw new ArgumentNullException("source", "source is null.");
 //           if (convert == null)
 //               throw new ArgumentNullException("convert", "convert is null.");
 //           if (convertBack == null)
 //               throw new ArgumentNullException("convertBack", "convertBack is null.");

 //           var translator = new LinkedCollection<TTarget>.TwoWayTranslator<TSource>(source, convert, convertBack);
 //           return translator.TargetCollection;
 //       }
 //   }


	///// <summary>
	///// This represents an observable collection that is linked to another observable collection of a different type. Changes in one will always affect the other.
	///// </summary>
	///// <typeparam name="TTarget">Type of item to be stored in this collection</typeparam>
	///// <remarks>Like a ReadOnlyObservableCollection, this won't be garbage collected until the underlying collection is collected.</remarks>
	//public class LinkedCollection<TTarget> : ExtendedObservableCollection<TTarget>
	//{
	//	TwoWayTranslator m_Translator;

	//	internal LinkedCollection(TwoWayTranslator translator)
	//	{
	//		m_Translator = translator;
	//	}

	//	/// <summary>
	//	/// This breaks the link between this class and the source class. Once broken the link cannot be reestablished.
	//	/// </summary>
	//	/// <remarks>Ensure BreakLink is called before this object goes out of scope, unless the target object is also going out of scope. Duplicate calls to BreakLink have no effect.</remarks>
	//	public void BreakLink()
	//	{
	//		if (m_Translator != null)
	//		{
	//			m_Translator.BreakLink();
	//			m_Translator = null;
	//		}
	//	}

	//	internal abstract class TwoWayTranslator
	//	{
	//		public abstract void BreakLink();
	//	}

	//	internal class TwoWayTranslator<TSource> : TwoWayTranslator
	//	{
	//		readonly Func<TSource, TTarget> m_Convert;

	//		readonly Func<TTarget, TSource> m_ConvertBack;

	//		readonly ObservableCollection<TSource> m_Source;

	//		readonly LinkedCollection<TTarget> m_Target;

	//		bool m_Updating;

	//		public TwoWayTranslator(ObservableCollection<TSource> source, Func<TSource, TTarget> convert, Func<TTarget, TSource> convertBack)
	//		{
	//			m_Target = new LinkedCollection<TTarget>(this);
	//			m_Source = source;
	//			m_ConvertBack = convertBack;
	//			m_Convert = convert;
	//			foreach (var item in m_Source)
	//				m_Target.Add(m_Convert(item));
	//			m_Source.CollectionChanged += Source_CollectionChanged;
	//			m_Target.CollectionChanged += Target_CollectionChanged;
	//		}

	//		public LinkedCollection<TTarget> TargetCollection
	//		{
	//			get { return m_Target; }
	//		}

	//		public override void BreakLink()
	//		{
	//			m_Source.CollectionChanged -= Source_CollectionChanged;
	//			m_Target.CollectionChanged -= Target_CollectionChanged;
	//		}

	//		void Source_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
	//		{
	//			if (m_Updating)
	//				return;
	//			try
	//			{
	//				m_Updating = true;
	//				switch (e.Action)
	//				{
	//					case NotifyCollectionChangedAction.Add:
 //                           var newList = (from i in e.NewItems.Cast<TSource>() select m_Convert(i)).ToList();
	//						m_Target.InsertRange(e.NewStartingIndex, newList);
	//						break;
	//					case NotifyCollectionChangedAction.Remove:
	//						m_Target.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
	//						break;
	//					case NotifyCollectionChangedAction.Reset:
	//						m_Target.Clear();
	//						foreach (var item in m_Source)
	//							m_Target.Add(m_Convert(item));
	//						break;
	//					default:
	//						//TODO - Handle the other cases for e.Action
	//						throw new NotImplementedException(string.Format(CultureInfo.InvariantCulture, "Action type {0} is not supported.", e.Action));
	//				}
	//			}
	//			finally
	//			{
	//				m_Updating = false;
	//			}
	//		}

	//		void Target_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
	//		{
	//			if (m_Updating)
	//				return;
	//			try
	//			{
	//				m_Updating = true;
	//				switch (e.Action)
	//				{
	//					case NotifyCollectionChangedAction.Add:
 //                           var newList = (from i in e.NewItems.Cast<TTarget>() select m_ConvertBack(i)).ToList();
	//						m_Source.InsertRange(e.NewStartingIndex, newList);
	//						break;
	//					case NotifyCollectionChangedAction.Remove:
	//						m_Source.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
	//						break;
	//					case NotifyCollectionChangedAction.Reset:
	//						m_Source.Clear();
	//						foreach (var item in m_Target)
	//							m_Source.Add(m_ConvertBack(item));
	//						break;
	//					default:
	//						//TODO - Handle the other cases for e.Action
	//						throw new NotImplementedException(string.Format(CultureInfo.InvariantCulture, "Action type {0} is not supported.", e.Action));
	//				}
	//			}
	//			finally
	//			{
	//				m_Updating = false;
	//			}
	//		}

	//	}
	//}
}
